package main

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"law-api-master/conf"
	"law-api-master/routers"
)

type UploadTester struct {
	callBackUrl string
	url         string
	keyFile     *os.File

	quit chan struct{}
}

func NewUploadTester(config *conf.TesterConf) *UploadTester {
	var router = "/api/upload/chain"
	var callbackRouter = "/api/callback"
	t := &UploadTester{
		callBackUrl: config.CallbackURL() + callbackRouter,
		url:         config.ServerURL() + router,
		keyFile:     config.KeyFile(),
		quit:        make(chan struct{}),
	}

	return t
}

//Close shutdown its test loop
func (ut *UploadTester) Close() {
	close(ut.quit)
}

//StartTester open its testloop
func (ut *UploadTester) StartTester() {
	go ut.loop()
}

func (ut *UploadTester) loop() {
	timer := time.NewTicker(1 * time.Minute)
	for {
		select {
		case <-timer.C:
			count := int(100)
			go ut.Do(10, count)

		case <-ut.quit:
			return
		}

	}
}

//Do use its keylist to send many request.
func (ut *UploadTester) Do(userNum int, listNum int) {
	filekeys, err := ut.loadKeyFile()
	if err != nil {
		fmt.Printf("load key error: %v\n", err)
		return
	}
	if len(filekeys) < userNum {
		fmt.Printf("user key not enough! need: %v, have:%v\n", userNum, len(filekeys))
		return
	}

	userKey := make([]string, 0)
	for i := 0; i < userNum; i++ {
		userKey = append(userKey, filekeys[i])
	}

	result := make(chan *routers.ChainResponse, len(userKey))
	for _, key := range userKey {
		go func(k string) {
			res := new(routers.ChainResponse)
			rb, err := ut.generateData(k, listNum)
			if err != nil {
				fmt.Printf("generate data err: %v\n", err)
				result <- res
				return
			}
			resb, err := ut.sendData(rb)
			if err != nil {
				fmt.Printf("send data err: %v\n", err)
				result <- res
				return
			}

			if err := json.Unmarshal(resb, res); err != nil {
				fmt.Printf("json parse err: %v\n", err)
			}
			result <- res

		}(key)
	}

	succeed := 0
	for i := 0; i < len(userKey); i++ {
		res := <-result
		if res == nil {
			continue
		}
		if res.Code == 0 {
			succeed++
		}
	}

	log.Printf("send upload request! total user: %v, succeed user: %v, send file: %v\n",
		len(userKey), succeed, listNum)
}

func (ut *UploadTester) loadKeyFile() ([]string, error) {
	keys := make([]string, 0)
	buR := bufio.NewReader(ut.keyFile)
	for {
		line, err := buR.ReadString('\n')
		key := strings.Replace(line, "\n", "", -1)
		keys = append(keys, key)

		//quit
		if err != nil {
			if err == io.EOF {
				return keys, nil
			}
			fmt.Printf("read error: %v\n", err)
			return keys, err
		}
	}
}

func (ut *UploadTester) generateData(key string, fileNum int) ([]byte, error) {
	request := &routers.ChainRequest{
		Url:     ut.callBackUrl,
		Privkey: key,
	}

	dats := make([]*routers.ChainRequestData, 0)
	now := strconv.FormatInt(time.Now().Unix(), 10)

	for i := 0; i < fileNum; i++ {
		dat := &routers.ChainRequestData{
			FileID:    hash256(now, i),
			Sha256:    hash256(now, i),
			Sm3:       hash256(now, i),
			Md5:       hash256(now, i),
			Timestamp: now,
		}
		dats = append(dats, dat)
	}
	request.Data = dats
	return json.Marshal(request)
}

func hash256(dat string, random int) string {
	hashFunc := sha256.New()
	hashFunc.Write([]byte(dat))

	rbyte := make([]byte, 8)
	binary.BigEndian.PutUint64(rbyte, uint64(random))

	return hex.EncodeToString(hashFunc.Sum(rbyte))
}

func (ut *UploadTester) sendData(data []byte) ([]byte, error) {
	r, err := http.Post(ut.url, "application/json;charset=utf-8", bytes.NewBuffer(data))
	if err != nil {
		log.Printf("send http err: %v\n", err)
		return nil, err
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("read http err: %v\n", err)
		return nil, err
	}
	return b, nil
}
